1from Quartz import * 2from Cocoa import * 3import Utilities 4import QuartzTextDrawing 5 6import sys 7import objc 8 9def getTextString(): 10 # These unicode values are the characters: Q, u, a, r, t, z, 11 # eighthnote, floral heart, black chess queen, and two CJK characters. 12 # Note: Create an NSString, because we'll use NSString-specific API's, otherwise 13 # we could just have used a python unicode object 14 return NSString.stringWithString_(u'\u0051\u0075\u0061\u0072\u0074\u007A\u266A\u2766\u265B\u3042\u304E') 15 16doPointDrawing=1 17 18def drawNSStringWithAttributes(): 19 textString = getTextString() 20 if doPointDrawing: 21 context = NSGraphicsContext.currentContext().graphicsPort() 22 23 # Text Line 1. Draw with default attributes. 24 p = NSMakePoint(20.0, 400.0) 25 26 # Draw text with default text attributes. The point supplied is 27 # not the text baseline but rather the lower-left corner of the box 28 # which bounds the text. 29 textString.drawAtPoint_withAttributes_(p, None) 30 31 if doPointDrawing: 32 Utilities.drawPoint(context, p) 33 34 # Text Line 2. Draw with a specific font and color. 35 36 # Position the text 50 units below the previous text. 37 p.y -= 50 38 39 # Set attributes to use when drawing the string. 40 stringAttributes = { 41 # Use the font with the PostScript name "Times-Roman" at 40 point. 42 NSFontAttributeName: NSFont.fontWithName_size_("Times-Roman", 40), 43 44 # Set the color attribute to an opaque red. 45 NSForegroundColorAttributeName: NSColor.colorWithCalibratedRed_green_blue_alpha_(0.663, 0, 0.031, 1.0) 46 } 47 48 # Draw the text. 49 textString.drawAtPoint_withAttributes_(p, stringAttributes) 50 51 if doPointDrawing: 52 Utilities.drawPoint(context, p) 53 54 # Text Line 3. Draw stroked text. 55 56 # Position the text 50 units below the previous text. 57 p.y -= 50 58 59 # Panther and later support stroke attributes. A positive value 60 # of the stroke width attribute produces text that is stroked rather 61 # than filled. 62 stringAttributes[NSStrokeWidthAttributeName] = 3.0 63 textString.drawAtPoint_withAttributes_(p, stringAttributes) 64 65 if doPointDrawing: 66 Utilities.drawPoint(context, p) 67 68 # Text Line 4. Draw with fill and stroke. 69 70 p.y -= 50 71 72 # Panther and later support stroke attributes. A negative value 73 # of the stroke width attribute results in text that is both filled 74 # and stroked. 75 stringAttributes[NSStrokeWidthAttributeName] = -3.0 76 # Set the stroke color attribute to black. 77 stringAttributes[NSStrokeColorAttributeName] = NSColor.colorWithCalibratedRed_green_blue_alpha_(0, 0, 0, 1.0) 78 79 textString.drawAtPoint_withAttributes_(p, stringAttributes) 80 81 if doPointDrawing: 82 Utilities.drawPoint(context, p) 83 84 # Text Line 5. Draw at baseline. 85 # Tiger and later support the drawWithRect method which allows 86 # string text drawing from a point on the text baseline. 87 p.y -= 50 88 rect = NSRect( 89 origin=p, 90 size=NSSize(0,0), 91 ) 92 textString.drawWithRect_options_attributes_( 93 rect, NSStringDrawingDisableScreenFontSubstitution, 94 stringAttributes) 95 96 if doPointDrawing: 97 Utilities.drawPoint(context, p) 98 99_myLayout = None 100_textStorage = None 101_myTextRange = None 102def drawWithNSLayout(): 103 global _myLayout, _textStorage, _myTextRange 104 105 if _myLayout is None: 106 # Initialize the text storage with the string to draw. 107 _textStorage = NSTextStorage.alloc().initWithString_(getTextString()) 108 # Initialize the layout manager to use with the text storage. 109 _myLayout = NSLayoutManager.alloc().init() 110 # Allocate and initialize a text container object. 111 textContainer = NSTextContainer.alloc().init() 112 # Add the text container to the layout. 113 _myLayout.addTextContainer_(textContainer) 114 # Release the text container since the layout retains it and 115 # this code no longer needs it. 116 del textContainer 117 # Add the layout to the text storage. 118 _textStorage.addLayoutManager_(_myLayout) 119 120 # Set attributes to use when drawing the string. 121 stringAttributes = { 122 # Use the font with the PostScript name "Times-Roman" at 40 point. 123 NSFontAttributeName: NSFont.fontWithName_size_("Times-Roman", 40), 124 125 # Set the text color attribute to an opaque red. 126 NSForegroundColorAttributeName: NSColor.colorWithCalibratedRed_green_blue_alpha_(0.663, 0, 0.031, 1.0), 127 } 128 129 # Create the range of text for the entire length of text 130 # in the textStorage object. 131 _myTextRange = NSMakeRange(0, _textStorage.length()) 132 # Set the attributes on the entire range of text. 133 _textStorage.setAttributes_range_(stringAttributes, _myTextRange) 134 135 # Set the point for drawing the layout. 136 p = NSMakePoint(20.0, 400.0) 137 138 # Draw the text range at the point. 139 _myLayout.drawGlyphsForGlyphRange_atPoint_(_myTextRange, p) 140 141 if doPointDrawing: 142 context = NSGraphicsContext.currentContext().graphicsPort() 143 Utilities.drawPoint(context, p) 144 145# The interface to the NSLayoutManager subclass. 146class MyNSLayoutManager (NSLayoutManager): 147 # The extra instance variables for this subclass. 148 _textMode = objc.ivar() 149 _fColor = objc.ivar() 150 _sColor = objc.ivar() 151 _yStartPosition = objc.ivar() 152 _lineWidth = objc.ivar() 153 _clippingDrawProc = objc.ivar() 154 _clippingInfo = objc.ivar() 155 156 # Public methods to set the special attributes 157 # of the MyNSLayoutManager instance. 158 def setTextMode_(self, textMode): 159 self._textMode = textMode 160 161 def setFillColor_(self, color): 162 self._fColor = color 163 164 def setStrokeColor_(self, color): 165 self._sColor = color 166 167 def setTextLineWidth_(self, width): 168 self._lineWidth = width 169 170 def setClippingDrawProc_withInfo_(self, clippingDrawProc, info): 171 self._clippingDrawProc = clippingDrawProc 172 self._clippingInfo = info 173 174 def init(self): 175 self = super(MyNSLayoutManager, self).init() 176 if self is None: 177 return None 178 179 180 # Initialize the custom instance variables. 181 self._textMode = kCGTextFill 182 self._fColor = None 183 self._sColor = None 184 self._yStartPosition = 0 185 self._lineWidth = 1 186 self._clippingDrawProc = None 187 self._clippingInfo = None 188 return self 189 190 # This code overrides this method to record the y coordinate 191 # to use as the True baseline for the text drawing. 192 def drawGlyphsForGlyphRange_atPoint_(self, glyphsToShow, origin): 193 self._yStartPosition = origin.y 194 super(MyNSLayoutManager, self).drawGlyphsForGlyphRange_atPoint_(glyphsToShow, origin) 195 196 # This is the rendering method of NSLayoutManager that the 197 # code overrides to perform its custom rendering. 198 def showPackedGlyphs_length_glyphRange_atPoint_font_color_printAdjustment_( 199 self, glyphs, glyphLen, glyphRange, point, font, color, printingAdjustment): 200 201 # Obtain the destination drawing context. 202 context = NSGraphicsContext.currentContext().graphicsPort() 203 204 # Adjust start position y value based on the adjusted y coordinate. 205 # This ensures the text baseline is at the starting position 206 # passed to drawGlyphsForGlyphRange. This technique won't work 207 # for super, subscripts, or underlines but that's OK for this example. 208 point.y = _yStartPosition 209 210 # The Quartz graphics state should be preserved by showPackedGlyphs. 211 CGContextSaveGState(context) 212 213 # Set the desired text drawing mode. 214 CGContextSetTextDrawingMode(context, self._textMode) 215 216 # Set the fill color if needed. 217 if (self._textMode == kCGTextFill or _self.textMode == kCGTextFillStroke or 218 self._textMode == kCGTextFillClip or _textMode == kCGTextFillStrokeClip): 219 if self._fColor is not None: 220 CGContextSetFillColorWithColor(context, self._fColor) 221 222 # Set the line width and the stroke color if needed. 223 if (self._textMode == kCGTextStroke or self._textMode == kCGTextFillStroke or 224 self._textMode == kCGTextStrokeClip or self._textMode == kCGTextFillStrokeClip): 225 CGContextSetLineWidth(context, self._lineWidth) 226 if self._sColor is not None: 227 CGContextSetStrokeColorWithColor(context, self._sColor) 228 229 # Now draw the text. Check whether to adjust for printing widths 230 # and if needed adjust extra character spacing accordingly. 231 if printingAdjustment.width != 0.0: 232 # If printingAdjustment width is non-zero then the text 233 # needs to be adjusted. printingAdjustment is the per character 234 # adjustment required for this piece of text. Because 235 # the Quartz text character spacing set is transformed by 236 # the text matrix, this code needs to factor out that effect 237 # prior to setting it. Cocoa sets the text matrix to account 238 # for the point size of the font so we factor that out of the 239 # per character width supplied here. 240 charAdjust = printingAdjustment.width / font.pointSize() 241 CGContextSetCharacterSpacing(context, charAdjust) 242 else: 243 CGContextSetCharacterSpacing(context, 0.0) 244 245 # Draw the glyphs. The total number of glyphs is the length 246 # of the glyphs string passed to showPackedGlyphs, divided by 2 247 # since there are two bytes per glyph. 248 CGContextShowGlyphsAtPoint(context, point.x, point.y, glyphs, glyphLen/2) 249 250 # If the text drawing mode requires clipping and there is 251 # a custom clipping proc, call it. This allows drawing through 252 # clipped text before the graphics state is restored. 253 if (self._textMode == kCGTextClip or self._textMode == kCGTextFillClip or 254 self._textMode == kCGTextStrokeClip or 255 self._textMode == kCGTextFillStrokeClip) and self._clippingDrawProc is not None: 256 257 self._clippingDrawProc(context, point.x, point.y, self._clippingInfo) 258 259 CGContextRestoreGState(context) 260 261def MyClipProc(c, x, y, info): 262 CGContextTranslateCTM(c, x, y) 263 CGContextSetStrokeColorWithColor(c, Utilities.getRGBOpaqueBlackColor()) 264 # Draw a grid of lines through the clip. 265 QuartzTextDrawing.drawGridLines(c); 266 267_myLayout = None 268_textStorage = None 269_myTextRange = None 270def drawWithCustomNSLayout(): 271 global _myLayout, _textStorage, _myTextRange 272 273 if _myLayout is None: 274 textContainer = NSTextContainer.alloc().init() 275 276 _textStorage = NSTextStorage.alloc().initWithString_(getTextString()) 277 # Create an instance of the MyNSLayoutManager subclass of NSLayoutManager. 278 _myLayout = MyNSLayoutManager.alloc().init() 279 _myLayout.addTextContainer_(textContainer) 280 # The layout retains the text container so this code can release it. 281 del textContainer 282 _textStorage.addLayoutManager_(_myLayout) 283 284 # Set attributes to use when drawing the string. 285 stringAttributes = { 286 # Use the font with the PostScript name "Times-Roman" at 40 point. 287 NSFontAttributeName: NSFont.fontWithName_size_("Times-Roman", 40), 288 } 289 290 # Create the range. 291 _myTextRange = NSMakeRange(0, _textStorage.length()) 292 # Set the attributes on the entire range of text. 293 _textStorage.setAttributes_range_(stringAttributes, _myTextRange) 294 295 p = NSMakePoint(20.0, 400.0) 296 297 # Set the custom attributes of the layout subclass so that 298 # the text will be filled with black. 299 _myLayout.setTextMode_(kCGTextFill) 300 _myLayout.setFillColor_(Utilities.getRGBOpaqueBlackColor()) 301 302 # Draw text line 1. 303 _myLayout.drawGlyphsForGlyphRange_atPoint_(_myTextRange, p) 304 305 if doPointDrawing: 306 context = NSGraphicsContext.currentContext().graphicsPort() 307 Utilities.drawPoint(context, p) 308 309 # Set the custom attributes of the layout subclass so that 310 # the text will be stroked with black. 311 _myLayout.setTextMode_(kCGTextStroke) 312 _myLayout.setStrokeColor_(Utilities.getRGBOpaqueBlackColor()) 313 _myLayout.setTextLineWidth_(2) 314 315 # Draw text line 2. 316 p.y -= 50; 317 _myLayout.drawGlyphsForGlyphRange_atPoint_(_myTextRange, p) 318 319 if doPointDrawing: 320 Utilities.drawPoint(context, p) 321 322 p.y -= 50; 323 324 # Set the custom attributes of the layout subclass so that 325 # the text will be filled and stroked and the fill color 326 # will be red. Since the stroke color hasn't changed it 327 # will be stroked with black. 328 _myLayout.setTextMode_(kCGTextFillStroke) 329 _myLayout.setFillColor_(Utilities.getRGBOpaqueRedColor()) 330 # Draw text line 3. 331 _myLayout.drawGlyphsForGlyphRange_atPoint_(_myTextRange, p) 332 333 if doPointDrawing: 334 Utilities.drawPoint(context, p) 335 336 p.y -= 50; 337 338 # Set the custom attributes of the layout subclass so that 339 # the text will be filled, stroked, then clipped. 340 _myLayout.setTextMode_(kCGTextFillStrokeClip) 341 342 # Set the clipping proc to MyClipProc which requires 343 # no info data. 344 _myLayout.setClippingDrawProc_withInfo_(MyClipProc, None) 345 346 # Draw text line 4. 347 _myLayout.drawGlyphsForGlyphRange_atPoint_(_myTextRange, p) 348 349 if doPointDrawing: 350 Utilities.drawPoint(context, p) 351 352 # Set the clipping proc to None for future drawing. 353 _myLayout.setClippingDrawProc_withInfo_(None, None) 354